package com.autumn.web.handlers;

import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import com.autumn.spring.boot.context.SrpingServiceContextHolder;
import com.autumn.util.StringUtils;
import com.autumn.util.UserFriendlyException;
import com.autumn.web.annotation.ContextServiceRequestHeader;

/**
 * 服务请求拦截处理器
 * 
 * @author 老码农
 *         <p>
 *         Description
 *         </p>
 * @date 2018-01-08 02:23:55
 */
public class ServiceRequestContextHandlerInterceptor extends WebMvcConfigurerAdapter implements HandlerInterceptor {

	@Autowired
	private WebApplicationContext applicationContext;

	private static Object HANDLER_METHOD_MAP_LOCK = new Object();

	private static Map<String, ServiceRequestContextHandlerInfo> HANDLER_METHOD_MAP = null;

	private ServiceRequestContextHandlerInfo findRequestHeader(HandlerMethod handlerMethod) {
		ContextServiceRequestHeader serviceHeader = handlerMethod.getMethod()
				.getAnnotation(ContextServiceRequestHeader.class);
		if (serviceHeader == null) {
			Class<?> type = handlerMethod.getMethod().getDeclaringClass();
			do {
				serviceHeader = type.getAnnotation(ContextServiceRequestHeader.class);
				if (serviceHeader != null) {
					break;
				}
				type = type.getSuperclass();
			} while (!type.equals(Object.class));
		}
		if (serviceHeader != null) {
			String name;
			if (StringUtils.isNullOrBlank(serviceHeader.headerParameterName())) {
				name = ContextServiceRequestHeader.HEADER_DEFAULT_PARAMETER_NAME;
			} else {
				name = serviceHeader.headerParameterName();
			}
			return new ServiceRequestContextHandlerInfo(name, serviceHeader.required(), handlerMethod);
		}
		return null;
	}

	private Map<String, ServiceRequestContextHandlerInfo> getHandlerMethodMap() {
		if (HANDLER_METHOD_MAP != null) {
			return HANDLER_METHOD_MAP;
		}
		synchronized (HANDLER_METHOD_MAP_LOCK) {
			if (HANDLER_METHOD_MAP != null) {
				return HANDLER_METHOD_MAP;
			}
			RequestMappingHandlerMapping mapping = applicationContext.getBean(RequestMappingHandlerMapping.class);
			Map<RequestMappingInfo, HandlerMethod> map = mapping.getHandlerMethods();
			HANDLER_METHOD_MAP = new ConcurrentHashMap<>(map.size());
			for (Map.Entry<RequestMappingInfo, HandlerMethod> entry : map.entrySet()) {			
				ServiceRequestContextHandlerInfo cfgReq = findRequestHeader(entry.getValue());
				if (cfgReq != null) {
					Set<String> patterns = entry.getKey().getPatternsCondition().getPatterns();
					for (String url : patterns) {
						HANDLER_METHOD_MAP.put(url.toLowerCase(), cfgReq);
					}
				}
			}
			return HANDLER_METHOD_MAP;
		}
	}

	@Override
	public void addInterceptors(InterceptorRegistry registry) {
		registry.addInterceptor(this);
	}

	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		SrpingServiceContextHolder.removeRequestServiceId();
		ServiceRequestContextHandlerInfo cfgReq = this.getHandlerMethodMap()
				.get(request.getServletPath().toLowerCase());
		if (cfgReq != null) {
			String serviceId = request.getHeader(cfgReq.getHeaderParameterName());
			if (!StringUtils.isNullOrBlank(serviceId)) {
				SrpingServiceContextHolder.setRequestServiceId(serviceId.trim());
			} else {
				if (cfgReq.isRequired()) {
					throw new UserFriendlyException(
							"请求头中(Header)，参数 " + cfgReq.getHeaderParameterName() + " 不能为 null 或空白值。");
				}
			}
		}
		return true;
	}

	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			ModelAndView modelAndView) throws Exception {

	}

	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
			throws Exception {
		SrpingServiceContextHolder.removeRequestServiceId();
	}

}
